package cn.demo.service;

import cn.demo.entity.Account;
import cn.demo.exception.MyException;
import cn.demo.mapper.AccountMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

@Slf4j
@Service
public class AccountService{
    @Autowired
    private AccountMapper accountMapper;

    @Autowired
    TransactionTemplate transactionTemplate;

    @Autowired
    PlatformTransactionManager transactionManager;

    /**
     * 模拟转账
     * @param fromAccountId
     * @param toAccountId
     * @param money
     * @throws Exception
     */
    @Transactional
    public void transferAccounts1(int fromAccountId, int toAccountId, int money) throws Exception {
        //扣款
        Account fromAccount = accountMapper.selectById(fromAccountId);
        fromAccount.setBalance(fromAccount.getBalance()-money);
        accountMapper.updateById(fromAccount);

        int a = 2 / 0;
        //加款
        Account toAccount = accountMapper.selectById(toAccountId);
        toAccount.setBalance(toAccount.getBalance()+money);
        accountMapper.updateById(toAccount);
        throw new Exception();
    }

    public void transferAccounts2(int fromAccountId, int toAccountId, int money) throws Exception {
        transferAccountsInner(fromAccountId,toAccountId,money);
    }

    @Transactional(rollbackFor = Exception.class)
    public void transferAccountsInner(int fromAccountId, int toAccountId, int money) throws Exception {
        //扣款
        Account fromAccount = accountMapper.selectById(fromAccountId);
        fromAccount.setBalance(fromAccount.getBalance()-money);
        accountMapper.updateById(fromAccount);

        int a = 2 / 0;
        //加款
        Account toAccount = accountMapper.selectById(toAccountId);
        toAccount.setBalance(toAccount.getBalance()+money);
        accountMapper.updateById(toAccount);
        throw new Exception();
    }

    @Transactional(propagation= Propagation.REQUIRED,rollbackFor = MyException.class)
    public void transferAccounts3(int fromAccountId, int toAccountId, int money) throws Exception {
        //扣款
        Account fromAccount = accountMapper.selectById(fromAccountId);
        fromAccount.setBalance(fromAccount.getBalance()-money);
        accountMapper.updateById(fromAccount);
        //加款
        Account toAccount = accountMapper.selectById(toAccountId);
        toAccount.setBalance(toAccount.getBalance()+money);
        accountMapper.updateById(toAccount);
        throw new Exception();
    }

    @Transactional
    public void transferAccounts4(int fromAccountId, int toAccountId, int money) throws Exception {
        //扣款
        Account fromAccount = accountMapper.selectById(fromAccountId);
        fromAccount.setBalance(fromAccount.getBalance()-money);
        accountMapper.updateById(fromAccount);

        int a = 2 / 0;
        //加款
        Account toAccount = accountMapper.selectById(toAccountId);
        toAccount.setBalance(toAccount.getBalance()+money);
        accountMapper.updateById(toAccount);
        throw new Exception();
    }

    @Transactional
    public void transferAccounts5(int fromAccountId, int toAccountId, int money) throws Exception {
        //扣款
        Account fromAccount = accountMapper.selectById(fromAccountId);
        fromAccount.setBalance(fromAccount.getBalance()-money);
        accountMapper.updateById(fromAccount);

        try {
            int a = 2 / 0;
        }catch (Exception e){
            e.printStackTrace();
        }
        //加款
        Account toAccount = accountMapper.selectById(toAccountId);
        toAccount.setBalance(toAccount.getBalance()+money);
        accountMapper.updateById(toAccount);
    }

    /**
     * 编程式事务：循环体中只回滚发生异常的
     */
    public void transferAccounts6(){
        List<Integer> arrayList = Arrays.asList(1,2,3);
        for (Integer id : arrayList) {
            Boolean result = transactionTemplate.execute(new TransactionCallback<Boolean>() {
                @Override
                public Boolean doInTransaction(TransactionStatus status) {
                    try {
                        Account account = accountMapper.selectById(id);
                        if(id == 2){
                            accountMapper.updateById(new Account(1,-1,""));
                            //模拟存库异常
                            account.setName("22222");
                            accountMapper.updateById(account);
                        }else {
                            account.setName("1");
                            accountMapper.updateById(account);
                        }
                        return true;
                    }catch (Exception e){
                        log.error(e.getMessage());
                        //手动开启事务回滚
                        status.setRollbackOnly();
                        return false;
                    }
                }
            });
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void transferAccounts7(int fromAccountId, int toAccountId, int money){
        new Thread(()->{
            // 新线程内异常不回滚
            Account fromAccount = accountMapper.selectById(fromAccountId);
            fromAccount.setBalance(fromAccount.getBalance()-money);
            accountMapper.updateById(fromAccount);
        }).start();

        // 方法内异常正常回滚
        Account fromAccount = accountMapper.selectById(toAccountId);
        fromAccount.setBalance(fromAccount.getBalance()+money);
        accountMapper.updateById(fromAccount);
        int a = 2 / 0;
    }

    @Transactional()
    public void transferAccounts8(int fromAccountId, int toAccountId, int money) throws IOException {
        // 方法内异常正常回滚
        Account fromAccount = accountMapper.selectById(toAccountId);
        fromAccount.setBalance(fromAccount.getBalance()+money);
        accountMapper.updateById(fromAccount);

        throw  new IOException("");
    }
}
